import Vue from 'vue';

function composedPath(el) {
	var path = [];
	while (el) {
		path.push(el);
		if (el.tagName === 'HTML') {
			path.push(document);
			path.push(window);
			return path;
		}
		el = el.parentElement;
	}
	return path;
}

function getItemInfo(e, el) {
	const elements = e.path || (e.composedPath && e.composedPath()) || composedPath(e.target);
	if (elements && elements.length > 0) {
		elements.unshift(e.target);
	}
	for (let i = 0; i < elements.length; i++) {
		const it = elements[i];
		if (!el.contains(it)) {
			return { key: null };
		}
		const key = it.getAttribute('data-select-key');
		const disabled = it.getAttribute('data-select-disabled') === 'true';
		if (key !== null) {
			return { key, disabled };
		}
	}
	return { key: null };
}

function toggleVal(key, val) {
	if (val[key]) {
		Vue['delete'](val, key);
	} else {
		Vue.set(val, key, true);
	}
}

function emit(vnode, list) {
	var eventName = 'multi-select-change';
	if (vnode.componentInstance) {
		vnode.componentInstance.$emit(eventName, {
			detail: list
		});
	} else {
		vnode.elm.dispatchEvent(
			new CustomEvent(eventName, {
				detail: list
			})
		);
	}
}

export default {
	bind: function (el, { expression, modifiers: { clickNotClean } }, vNode) {
		let hovertKeys = null;
		const click = (e) => {
			// 按着 ctrl 进行选择
			if (e.ctrlKey || e.metaKey) {
				const { key, disabled } = getItemInfo(e, el);
				if (key === null || disabled) {
					return;
				}
				toggleVal(key, vNode.context[expression]);
				emit(vNode, [key]);
			}
		};
		const mousedown = (e) => {
			// 鼠标左键按下
			if (e.button !== 0 || e.ctrlKey || e.metaKey) {
				return;
			}
			const { key, disabled } = getItemInfo(e, el);
			if (key === null || disabled) {
				return;
			}
			hovertKeys = [key];
			if (clickNotClean) {
				toggleVal(key, vNode.context[expression]);
				emit(vNode, [key]);
			} else {
				const keys = Object.keys(vNode.context[expression]);
				vNode.context[expression] = Vue.observable({ [key]: true });
				if (keys.length === 0) {
					emit(vNode, [key]);
				} else {
					const changeKeys = keys.filter((it) => it !== key);
					if (changeKeys.length) {
						emit(vNode, changeKeys);
					}
				}
			}
		};
		const mousemove = (e) => {
			if (!hovertKeys) {
				return;
			}
			const { key, disabled } = getItemInfo(e, el);
			if (key === null || disabled) {
				return;
			}
			const index = hovertKeys.lastIndexOf(key);
			if (index === hovertKeys.length - 1) {
				return;
			}
			// 取消文字选中
			if (window.getSelection) {
				window.getSelection().removeAllRanges();
			} else {
				document.selection.empty();
			}
			if (index === -1) {
				hovertKeys.push(key);
				toggleVal(key, vNode.context[expression]);
				emit(vNode, [key]);
				return;
			}
			const delKeys = hovertKeys.splice(index + 1);
			delKeys.forEach((k) => {
				toggleVal(k, vNode.context[expression]);
				emit(vNode, [k]);
			});
		};
		const mouseup = (e) => {
			hovertKeys = null;
		};
		const mouseleave = (e) => {
			hovertKeys = null;
		};
		el.__vueMultiSelect__ = { click, mousedown, mousemove, mouseup, mouseleave };
		el.addEventListener('click', click);
		el.addEventListener('mousedown', mousedown);
		el.addEventListener('mousemove', mousemove);
		el.addEventListener('mouseleave', mouseleave);
		document.addEventListener('mouseup', mouseup);
	},

	unbind: function (el, binding) {
		if (!el.__vueMultiSelect__) {
			return;
		}
		Object.keys(el.__vueMultiSelect__).forEach((name) => {
			if (name === 'mouseup') {
				document.removeEventListener(name, el.__vueMultiSelect__[name]);
			}
			el.removeEventListener(name, el.__vueMultiSelect__[name]);
		});
		delete el.__vueMultiSelect__;
	}
}

/**
 * CustomEvent constructor polyfill for IE
 */
;
(function () {
	if (typeof window.CustomEvent === 'function') {
		// 如果不是IE
		return;
	}

	var CustomEvent = function (event, params) {
		params = params || {
			bubbles: false,
			cancelable: false,
			detail: undefined
		};
		var evt = document.createEvent('CustomEvent');
		evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
		return evt;
	};

	CustomEvent.prototype = window.Event.prototype;

	window.CustomEvent = CustomEvent;
})();
